/*=============================================================================
# FileName    :	spi_drive.svh
# Author      :	author
# Email       :	email@email.com
# Description : 主要用来测试spi slave的实现
# Version     :	1.0
# LastChange  :	2018-05-05 14:32:51
# ChangeLog   :
=============================================================================*/
`ifndef SPI_DRIVE_SVH
`define SPI_DRIVE_SVH

class spi_drive #(width = 8);
    virtual spi_bfm # (.WIDTH (width), .FREQ(1_000_000) ) bfm;
    rand bit [width-1:00] data[];

    constraint c
    {
        data.size() == 5;
        //foreach(data[i])
            //data[i] inside {[1:255]};
    }

    function new(virtual spi_bfm # (.WIDTH (width), .FREQ(1_000_000) )  b);
        bfm = b;
    endfunction

    //virtual function bit [07:00] calc_check(bit [07:00] data[]);
    //endfunction

    /*-------------------------------------------------------------------------
    //description   : 配置spi工作参数
    //parameter     :
    //others        :
    -------------------------------------------------------------------------*/
    extern task config_mode(bit [07:00] para, int freq);

    extern virtual task send_frame();

    extern task send_random_packet(int len);
    /*-------------------------------------------------------------------------
    //description   : 模拟 spi master 发送一个单位数据
    -------------------------------------------------------------------------*/
    extern task send_unit(bit [width-1:00] data);
    /*-------------------------------------------------------------------------
    //description   : 模拟 spi master 从 slave上读取一个单位数据，需要给出时钟
    //parameter     :
    //others        :
    -------------------------------------------------------------------------*/
    extern task read_byte();
endclass

task spi_drive::config_mode(bit [07:00] para, int freq);
    bfm.config_reg = para;
    if(bfm.config_reg[1])
        bfm.sclk = 1;
    else
        bfm.sclk = 0;
    $display("--------spi freq is %d--------\n--------cpol is %d, cpha is %d--------", bfm.FREQ, para[1], para[0]);
endtask

task spi_drive::send_frame();
    assert(this.randomize());
    //bfm.rxd = 1;
    #100ns
    for(int i = 0; i < $size(this.data); i = i + 1 )
    begin
        $display("spi master write data is 0x%x", this.data[i]);
        send_unit(this.data[i]);
    end
endtask

task spi_drive::send_random_packet(int len);
    bit [07:00] data;
    for(int i = 0; i < len; i = i + 1 )
    begin
        data = $urandom_range(0, 255);
        $display("at %t, spi write data is 0x%x", $time, data);
        send_unit(data);
    end
endtask

task spi_drive::send_unit(bit [width-1:00] data);
    if(bfm.config_reg[01:00] == 2'b00)     // mode1
    begin
        $display("spi master send data is %x", data);
        for(int i = 0; i < $size(data); i = i + 1 )
        begin
            bfm.sclk = 0; bfm.mosi = data[$size(data)-1-i]; #(1s/bfm.FREQ/2);
            bfm.sclk = 1; #(1s/bfm.FREQ/2);
        end
        bfm.sclk = 0;
        bfm.mosi = 0;
    end
    else if(bfm.config_reg[01:00] == 2'b01)     // mode3
    begin
        $display("spi master send data is %x", data);
        for(int i = 0; i < $size(data); i = i + 1 )
        begin
            bfm.sclk = 1; bfm.mosi = data[$size(data)-1-i]; #(1s/bfm.FREQ/2);
            bfm.sclk = 0; #(1s/bfm.FREQ/2);
        end
        bfm.sclk = 0;
        bfm.mosi = 0;
    end
    else if(bfm.config_reg[01:00] == 2'b10)     // mode2
    begin
        $display("spi master send data is %x", data);
        for(int i = 0; i < $size(data); i = i + 1 )
        begin
            bfm.sclk = 1; bfm.mosi = data[$size(data)-1-i]; #(1s/bfm.FREQ/2);
            bfm.sclk = 0; #(1s/bfm.FREQ/2);
        end
        bfm.sclk = 1;
        bfm.mosi = 0;
    end
    else if(bfm.config_reg[01:00] == 2'b11)     // mode1
    begin
        $display("spi master send data is %x", data);
        for(int i = 0; i < $size(data); i = i + 1 )
        begin
            bfm.sclk = 0; bfm.mosi = data[$size(data)-1-i]; #(1s/bfm.FREQ/2);
            bfm.sclk = 1; #(1s/bfm.FREQ/2);
        end
        bfm.sclk = 1;
        bfm.mosi = 0;
    end
endtask

task spi_drive::read_byte();
    bit [07:00]             recv_data;
    if(bfm.config_reg[01:00] == 2'b00)     // mode1
    begin
        for(int i = 0; i < $size(recv_data); i = i + 1 )
        begin
            @ (posedge bfm.sclk);
            $display("i = %d", i);
            recv_data[$size(recv_data)-1-i] = bfm.miso;
        end
        $display("spi master receive data is %x", recv_data);
    end
    else if(bfm.config_reg[01:00] == 2'b01)     // mode3
    begin
        for(int i = 0; i < $size(recv_data); i = i + 1 )
        begin
            @ (negedge bfm.sclk);
            recv_data[$size(recv_data)-1-i] = bfm.miso;
        end
        $display("spi master receive data is %x", recv_data);
    end
    else if(bfm.config_reg[01:00] == 2'b10)     // mode2
    begin
        for(int i = 0; i < $size(recv_data); i = i + 1 )
        begin
            @ (posedge bfm.sclk);
            recv_data[$size(recv_data)-1-i] = bfm.miso;
        end
        $display("spi master receive data is %x", recv_data);
    end
    else if(bfm.config_reg[01:00] == 2'b11)     // mode1
    begin
        for(int i = 0; i < $size(recv_data); i = i + 1 )
        begin
            @ (negedge bfm.sclk);
            recv_data[$size(recv_data)-1-i] = bfm.miso;
        end
        $display("spi master receive data is %x", recv_data);
    end
endtask
`endif
